/*
 * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
/*
 * Copyright 2001-2004 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * $Id: Mode.java,v 1.2.4.1 2005/09/19 05:18:11 pvedula Exp $
 */

package com.sun.org.apache.xalan.internal.xsltc.compiler;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Vector;

import com.sun.org.apache.bcel.internal.generic.Instruction;
import com.sun.org.apache.bcel.internal.generic.BranchHandle;
import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
import com.sun.org.apache.bcel.internal.generic.DUP;
import com.sun.org.apache.bcel.internal.generic.GOTO_W;
import com.sun.org.apache.bcel.internal.generic.IFLT;
import com.sun.org.apache.bcel.internal.generic.ILOAD;
import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
import com.sun.org.apache.bcel.internal.generic.ISTORE;
import com.sun.org.apache.bcel.internal.generic.InstructionHandle;
import com.sun.org.apache.bcel.internal.generic.InstructionList;
import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
import com.sun.org.apache.bcel.internal.generic.SWITCH;
import com.sun.org.apache.bcel.internal.generic.TargetLostException;
import com.sun.org.apache.bcel.internal.util.InstructionFinder;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NamedMethodGenerator;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
import com.sun.org.apache.xml.internal.dtm.Axis;
import com.sun.org.apache.xml.internal.dtm.DTM;

/**
 * Mode gathers all the templates belonging to a given mode;
 * it is responsible for generating an appropriate
 * applyTemplates + (mode name) method in the translet.
 *
 * @author Jacek Ambroziak
 * @author Santiago Pericas-Geertsen
 * @author Morten Jorgensen
 * @author Erwin Bolwidt <ejb@klomp.org>
 * @author G. Todd Miller
 */
final class Mode implements Constants {

  /**
   * The name of this mode as defined in the stylesheet.
   */
  private final QName _name;

  /**
   * A reference to the stylesheet object that owns this mode.
   */
  private final Stylesheet _stylesheet;

  /**
   * The name of the method in which this mode is compiled.
   */
  private final String _methodName;

  /**
   * A vector of all the templates in this mode.
   */
  private Vector _templates;

  /**
   * Group for patterns with node()-type kernel and child axis.
   */
  private Vector _childNodeGroup = null;

  /**
   * Test sequence for patterns with node()-type kernel and child axis.
   */
  private TestSeq _childNodeTestSeq = null;

  /**
   * Group for patterns with node()-type kernel and attribute axis.
   */
  private Vector _attribNodeGroup = null;

  /**
   * Test sequence for patterns with node()-type kernel and attribute axis.
   */
  private TestSeq _attribNodeTestSeq = null;

  /**
   * Group for patterns with id() or key()-type kernel.
   */
  private Vector _idxGroup = null;

  /**
   * Test sequence for patterns with id() or key()-type kernel.
   */
  private TestSeq _idxTestSeq = null;

  /**
   * Group for patterns with any other kernel type.
   */
  private Vector[] _patternGroups;

  /**
   * Test sequence for patterns with any other kernel type.
   */
  private TestSeq[] _testSeq;


  /**
   * A mapping between templates and test sequences.
   */
  private Hashtable _neededTemplates = new Hashtable();

  /**
   * A mapping between named templates and Mode objects.
   */
  private Hashtable _namedTemplates = new Hashtable();

  /**
   * A mapping between templates and instruction handles.
   */
  private Hashtable _templateIHs = new Hashtable();

  /**
   * A mapping between templates and instruction lists.
   */
  private Hashtable _templateILs = new Hashtable();

  /**
   * A reference to the pattern matching the root node.
   */
  private LocationPathPattern _rootPattern = null;

  /**
   * Stores ranges of template precendences for the compilation
   * of apply-imports (a Hashtable for historical reasons).
   */
  private Hashtable _importLevels = null;

  /**
   * A mapping between key names and keys.
   */
  private Hashtable _keys = null;

  /**
   * Variable index for the current node used in code generation.
   */
  private int _currentIndex;

  /**
   * Creates a new Mode.
   *
   * @param name A textual representation of the mode's QName
   * @param stylesheet The Stylesheet in which the mode occured
   * @param suffix A suffix to append to the method name for this mode (normally a sequence number -
   * still in a String).
   */
  public Mode(QName name, Stylesheet stylesheet, String suffix) {
    _name = name;
    _stylesheet = stylesheet;
    _methodName = APPLY_TEMPLATES + suffix;
    _templates = new Vector();
    _patternGroups = new Vector[32];
  }

  /**
   * Returns the name of the method (_not_ function) that will be
   * compiled for this mode. Normally takes the form 'applyTemplates()'
   * or * 'applyTemplates2()'.
   *
   * @return Method name for this mode
   */
  public String functionName() {
    return _methodName;
  }

  public String functionName(int min, int max) {
    if (_importLevels == null) {
      _importLevels = new Hashtable();
    }
    _importLevels.put(new Integer(max), new Integer(min));
    return _methodName + '_' + max;
  }

  /**
   * Shortcut to get the class compiled for this mode (will be inlined).
   */
  private String getClassName() {
    return _stylesheet.getClassName();
  }

  public Stylesheet getStylesheet() {
    return _stylesheet;
  }

  public void addTemplate(Template template) {
    _templates.addElement(template);
  }

  private Vector quicksort(Vector templates, int p, int r) {
    if (p < r) {
      final int q = partition(templates, p, r);
      quicksort(templates, p, q);
      quicksort(templates, q + 1, r);
    }
    return templates;
  }

  private int partition(Vector templates, int p, int r) {
    final Template x = (Template) templates.elementAt(p);
    int i = p - 1;
    int j = r + 1;
    while (true) {
      while (x.compareTo((Template) templates.elementAt(--j)) > 0) {
        ;
      }
      while (x.compareTo((Template) templates.elementAt(++i)) < 0) {
        ;
      }
      if (i < j) {
        templates.set(j, templates.set(i, templates.elementAt(j)));
      } else {
        return j;
      }
    }
  }

  /**
   * Process all the test patterns in this mode
   */
  public void processPatterns(Hashtable keys) {
    _keys = keys;

/*
System.out.println("Before Sort " + _name);
for (int i = 0; i < _templates.size(); i++) {
    System.out.println("name = " + ((Template)_templates.elementAt(i)).getName());
    System.out.println("pattern = " + ((Template)_templates.elementAt(i)).getPattern());
    System.out.println("priority = " + ((Template)_templates.elementAt(i)).getPriority());
    System.out.println("position = " + ((Template)_templates.elementAt(i)).getPosition());
}
*/

    _templates = quicksort(_templates, 0, _templates.size() - 1);

/*
System.out.println("\n After Sort " + _name);
for (int i = 0; i < _templates.size(); i++) {
    System.out.println("name = " + ((Template)_templates.elementAt(i)).getName());
    System.out.println("pattern = " + ((Template)_templates.elementAt(i)).getPattern());
    System.out.println("priority = " + ((Template)_templates.elementAt(i)).getPriority());
    System.out.println("position = " + ((Template)_templates.elementAt(i)).getPosition());
}
*/

    // Traverse all templates
    final Enumeration templates = _templates.elements();
    while (templates.hasMoreElements()) {
      // Get the next template
      final Template template = (Template) templates.nextElement();

            /*
             * Add this template to a table of named templates if it has a name.
             * If there are multiple templates with the same name, all but one
             * (the one with highest priority) will be disabled.
             */
      if (template.isNamed() && !template.disabled()) {
        _namedTemplates.put(template, this);
      }

      // Add this template to a test sequence if it has a pattern
      final Pattern pattern = template.getPattern();
      if (pattern != null) {
        flattenAlternative(pattern, template, keys);
      }
    }
    prepareTestSequences();
  }

  /**
   * This method will break up alternative patterns (ie. unions of patterns,
   * such as match="A/B | C/B") and add the basic patterns to their
   * respective pattern groups.
   */
  private void flattenAlternative(Pattern pattern,
      Template template,
      Hashtable keys) {
    // Patterns on type id() and key() are special since they do not have
    // any kernel node type (it can be anything as long as the node is in
    // the id's or key's index).
    if (pattern instanceof IdKeyPattern) {
      final IdKeyPattern idkey = (IdKeyPattern) pattern;
      idkey.setTemplate(template);
      if (_idxGroup == null) {
        _idxGroup = new Vector();
      }
      _idxGroup.add(pattern);
    }
    // Alternative patterns are broken up and re-processed recursively
    else if (pattern instanceof AlternativePattern) {
      final AlternativePattern alt = (AlternativePattern) pattern;
      flattenAlternative(alt.getLeft(), template, keys);
      flattenAlternative(alt.getRight(), template, keys);
    }
    // Finally we have a pattern that can be added to a test sequence!
    else if (pattern instanceof LocationPathPattern) {
      final LocationPathPattern lpp = (LocationPathPattern) pattern;
      lpp.setTemplate(template);
      addPatternToGroup(lpp);
    }
  }

  /**
   * Group patterns by NodeTests of their last Step
   * Keep them sorted by priority within group
   */
  private void addPatternToGroup(final LocationPathPattern lpp) {
    // id() and key()-type patterns do not have a kernel type
    if (lpp instanceof IdKeyPattern) {
      addPattern(-1, lpp);
    }
    // Otherwise get the kernel pattern from the LPP
    else {
      // kernel pattern is the last (maybe only) Step
      final StepPattern kernel = lpp.getKernelPattern();
      if (kernel != null) {
        addPattern(kernel.getNodeType(), lpp);
      } else if (_rootPattern == null ||
          lpp.noSmallerThan(_rootPattern)) {
        _rootPattern = lpp;
      }
    }
  }

  /**
   * Adds a pattern to a pattern group
   */
  private void addPattern(int kernelType, LocationPathPattern pattern) {
    // Make sure the array of pattern groups is long enough
    final int oldLength = _patternGroups.length;
    if (kernelType >= oldLength) {
      Vector[] newGroups = new Vector[kernelType * 2];
      System.arraycopy(_patternGroups, 0, newGroups, 0, oldLength);
      _patternGroups = newGroups;
    }

    // Find the vector to put this pattern into
    Vector patterns;

    if (kernelType == DOM.NO_TYPE) {
      if (pattern.getAxis() == Axis.ATTRIBUTE) {
        patterns = (_attribNodeGroup == null) ?
            (_attribNodeGroup = new Vector(2)) : _attribNodeGroup;
      } else {
        patterns = (_childNodeGroup == null) ?
            (_childNodeGroup = new Vector(2)) : _childNodeGroup;
      }
    } else {
      patterns = (_patternGroups[kernelType] == null) ?
          (_patternGroups[kernelType] = new Vector(2)) :
          _patternGroups[kernelType];
    }

    if (patterns.size() == 0) {
      patterns.addElement(pattern);
    } else {
      boolean inserted = false;
      for (int i = 0; i < patterns.size(); i++) {
        final LocationPathPattern lppToCompare =
            (LocationPathPattern) patterns.elementAt(i);

        if (pattern.noSmallerThan(lppToCompare)) {
          inserted = true;
          patterns.insertElementAt(pattern, i);
          break;
        }
      }
      if (inserted == false) {
        patterns.addElement(pattern);
      }
    }
  }

  /**
   * Complete test sequences of a given type by adding all patterns
   * from a given group.
   */
  private void completeTestSequences(int nodeType, Vector patterns) {
    if (patterns != null) {
      if (_patternGroups[nodeType] == null) {
        _patternGroups[nodeType] = patterns;
      } else {
        final int m = patterns.size();
        for (int j = 0; j < m; j++) {
          addPattern(nodeType,
              (LocationPathPattern) patterns.elementAt(j));
        }
      }
    }
  }

  /**
   * Build test sequences. The first step is to complete the test sequences
   * by including patterns of "*" and "node()" kernel to all element test
   * sequences, and of "@*" to all attribute test sequences.
   */
  private void prepareTestSequences() {
    final Vector starGroup = _patternGroups[DTM.ELEMENT_NODE];
    final Vector atStarGroup = _patternGroups[DTM.ATTRIBUTE_NODE];

    // Complete test sequence for "text()" with "child::node()"
    completeTestSequences(DTM.TEXT_NODE, _childNodeGroup);

    // Complete test sequence for "*" with "child::node()"
    completeTestSequences(DTM.ELEMENT_NODE, _childNodeGroup);

    // Complete test sequence for "pi()" with "child::node()"
    completeTestSequences(DTM.PROCESSING_INSTRUCTION_NODE, _childNodeGroup);

    // Complete test sequence for "comment()" with "child::node()"
    completeTestSequences(DTM.COMMENT_NODE, _childNodeGroup);

    // Complete test sequence for "@*" with "attribute::node()"
    completeTestSequences(DTM.ATTRIBUTE_NODE, _attribNodeGroup);

    final Vector names = _stylesheet.getXSLTC().getNamesIndex();
    if (starGroup != null || atStarGroup != null ||
        _childNodeGroup != null || _attribNodeGroup != null) {
      final int n = _patternGroups.length;

      // Complete test sequence for user-defined types
      for (int i = DTM.NTYPES; i < n; i++) {
        if (_patternGroups[i] == null) {
          continue;
        }

        final String name = (String) names.elementAt(i - DTM.NTYPES);

        if (isAttributeName(name)) {
          // If an attribute then copy "@*" to its test sequence
          completeTestSequences(i, atStarGroup);

          // And also copy "attribute::node()" to its test sequence
          completeTestSequences(i, _attribNodeGroup);
        } else {
          // If an element then copy "*" to its test sequence
          completeTestSequences(i, starGroup);

          // And also copy "child::node()" to its test sequence
          completeTestSequences(i, _childNodeGroup);
        }
      }
    }

    _testSeq = new TestSeq[DTM.NTYPES + names.size()];

    final int n = _patternGroups.length;
    for (int i = 0; i < n; i++) {
      final Vector patterns = _patternGroups[i];
      if (patterns != null) {
        final TestSeq testSeq = new TestSeq(patterns, i, this);
// System.out.println("testSeq[" + i + "] = " + testSeq);
        testSeq.reduce();
        _testSeq[i] = testSeq;
        testSeq.findTemplates(_neededTemplates);
      }
    }

    if (_childNodeGroup != null && _childNodeGroup.size() > 0) {
      _childNodeTestSeq = new TestSeq(_childNodeGroup, -1, this);
      _childNodeTestSeq.reduce();
      _childNodeTestSeq.findTemplates(_neededTemplates);
    }

/*
        if (_attribNodeGroup != null && _attribNodeGroup.size() > 0) {
            _attribNodeTestSeq = new TestSeq(_attribNodeGroup, -1, this);
            _attribNodeTestSeq.reduce();
            _attribNodeTestSeq.findTemplates(_neededTemplates);
        }
*/

    if (_idxGroup != null && _idxGroup.size() > 0) {
      _idxTestSeq = new TestSeq(_idxGroup, this);
      _idxTestSeq.reduce();
      _idxTestSeq.findTemplates(_neededTemplates);
    }

    if (_rootPattern != null) {
      // doesn't matter what is 'put', only key matters
      _neededTemplates.put(_rootPattern.getTemplate(), this);
    }
  }

  private void compileNamedTemplate(Template template,
      ClassGenerator classGen) {
    final ConstantPoolGen cpg = classGen.getConstantPool();
    final InstructionList il = new InstructionList();
    String methodName = Util.escape(template.getName().toString());

    int numParams = 0;
    if (template.isSimpleNamedTemplate()) {
      Vector parameters = template.getParameters();
      numParams = parameters.size();
    }

    // Initialize the types and names arrays for the NamedMethodGenerator.
    com.sun.org.apache.bcel.internal.generic.Type[] types =
        new com.sun.org.apache.bcel.internal.generic.Type[4 + numParams];
    String[] names = new String[4 + numParams];
    types[0] = Util.getJCRefType(DOM_INTF_SIG);
    types[1] = Util.getJCRefType(NODE_ITERATOR_SIG);
    types[2] = Util.getJCRefType(TRANSLET_OUTPUT_SIG);
    types[3] = com.sun.org.apache.bcel.internal.generic.Type.INT;
    names[0] = DOCUMENT_PNAME;
    names[1] = ITERATOR_PNAME;
    names[2] = TRANSLET_OUTPUT_PNAME;
    names[3] = NODE_PNAME;

    // For simple named templates, the signature of the generated method
    // is not fixed. It depends on the number of parameters declared in the
    // template.
    for (int i = 4; i < 4 + numParams; i++) {
      types[i] = Util.getJCRefType(OBJECT_SIG);
      names[i] = "param" + String.valueOf(i - 4);
    }

    NamedMethodGenerator methodGen =
        new NamedMethodGenerator(ACC_PUBLIC,
            com.sun.org.apache.bcel.internal.generic.Type.VOID,
            types, names, methodName,
            getClassName(), il, cpg);

    il.append(template.compile(classGen, methodGen));
    il.append(RETURN);

    classGen.addMethod(methodGen);
  }

  private void compileTemplates(ClassGenerator classGen,
      MethodGenerator methodGen,
      InstructionHandle next) {
    Enumeration templates = _namedTemplates.keys();
    while (templates.hasMoreElements()) {
      final Template template = (Template) templates.nextElement();
      compileNamedTemplate(template, classGen);
    }

    templates = _neededTemplates.keys();
    while (templates.hasMoreElements()) {
      final Template template = (Template) templates.nextElement();
      if (template.hasContents()) {
        // !!! TODO templates both named and matched
        InstructionList til = template.compile(classGen, methodGen);
        til.append(new GOTO_W(next));
        _templateILs.put(template, til);
        _templateIHs.put(template, til.getStart());
      } else {
        // empty template
        _templateIHs.put(template, next);
      }
    }
  }

  private void appendTemplateCode(InstructionList body) {
    final Enumeration templates = _neededTemplates.keys();
    while (templates.hasMoreElements()) {
      final Object iList =
          _templateILs.get(templates.nextElement());
      if (iList != null) {
        body.append((InstructionList) iList);
      }
    }
  }

  private void appendTestSequences(InstructionList body) {
    final int n = _testSeq.length;
    for (int i = 0; i < n; i++) {
      final TestSeq testSeq = _testSeq[i];
      if (testSeq != null) {
        InstructionList il = testSeq.getInstructionList();
        if (il != null) {
          body.append(il);
        }
        // else trivial TestSeq
      }
    }
  }

  public static void compileGetChildren(ClassGenerator classGen,
      MethodGenerator methodGen,
      int node) {
    final ConstantPoolGen cpg = classGen.getConstantPool();
    final InstructionList il = methodGen.getInstructionList();
    final int git = cpg.addInterfaceMethodref(DOM_INTF,
        GET_CHILDREN,
        GET_CHILDREN_SIG);
    il.append(methodGen.loadDOM());
    il.append(new ILOAD(node));
    il.append(new INVOKEINTERFACE(git, 2));
  }

  /**
   * Compiles the default handling for DOM elements: traverse all children
   */
  private InstructionList compileDefaultRecursion(ClassGenerator classGen,
      MethodGenerator methodGen,
      InstructionHandle next) {
    final ConstantPoolGen cpg = classGen.getConstantPool();
    final InstructionList il = new InstructionList();
    final String applyTemplatesSig = classGen.getApplyTemplatesSig();
    final int git = cpg.addInterfaceMethodref(DOM_INTF,
        GET_CHILDREN,
        GET_CHILDREN_SIG);
    final int applyTemplates = cpg.addMethodref(getClassName(),
        functionName(),
        applyTemplatesSig);
    il.append(classGen.loadTranslet());
    il.append(methodGen.loadDOM());

    il.append(methodGen.loadDOM());
    il.append(new ILOAD(_currentIndex));
    il.append(new INVOKEINTERFACE(git, 2));
    il.append(methodGen.loadHandler());
    il.append(new INVOKEVIRTUAL(applyTemplates));
    il.append(new GOTO_W(next));
    return il;
  }

  /**
   * Compiles the default action for DOM text nodes and attribute nodes:
   * output the node's text value
   */
  private InstructionList compileDefaultText(ClassGenerator classGen,
      MethodGenerator methodGen,
      InstructionHandle next) {
    final ConstantPoolGen cpg = classGen.getConstantPool();
    final InstructionList il = new InstructionList();

    final int chars = cpg.addInterfaceMethodref(DOM_INTF,
        CHARACTERS,
        CHARACTERS_SIG);
    il.append(methodGen.loadDOM());
    il.append(new ILOAD(_currentIndex));
    il.append(methodGen.loadHandler());
    il.append(new INVOKEINTERFACE(chars, 3));
    il.append(new GOTO_W(next));
    return il;
  }

  private InstructionList compileNamespaces(ClassGenerator classGen,
      MethodGenerator methodGen,
      boolean[] isNamespace,
      boolean[] isAttribute,
      boolean attrFlag,
      InstructionHandle defaultTarget) {
    final XSLTC xsltc = classGen.getParser().getXSLTC();
    final ConstantPoolGen cpg = classGen.getConstantPool();

    // Append switch() statement - namespace test dispatch loop
    final Vector namespaces = xsltc.getNamespaceIndex();
    final Vector names = xsltc.getNamesIndex();
    final int namespaceCount = namespaces.size() + 1;
    final int namesCount = names.size();

    final InstructionList il = new InstructionList();
    final int[] types = new int[namespaceCount];
    final InstructionHandle[] targets = new InstructionHandle[types.length];

    if (namespaceCount > 0) {
      boolean compiled = false;

      // Initialize targets for namespace() switch statement
      for (int i = 0; i < namespaceCount; i++) {
        targets[i] = defaultTarget;
        types[i] = i;
      }

      // Add test sequences for known namespace types
      for (int i = DTM.NTYPES; i < (DTM.NTYPES + namesCount); i++) {
        if ((isNamespace[i]) && (isAttribute[i] == attrFlag)) {
          String name = (String) names.elementAt(i - DTM.NTYPES);
          String namespace = name.substring(0, name.lastIndexOf(':'));
          final int type = xsltc.registerNamespace(namespace);

          if ((i < _testSeq.length) &&
              (_testSeq[i] != null)) {
            targets[type] =
                (_testSeq[i]).compile(classGen,
                    methodGen,
                    defaultTarget);
            compiled = true;
          }
        }
      }

      // Return "null" if no test sequences were compiled
      if (!compiled) {
        return (null);
      }

      // Append first code in applyTemplates() - get type of current node
      final int getNS = cpg.addInterfaceMethodref(DOM_INTF,
          "getNamespaceType",
          "(I)I");
      il.append(methodGen.loadDOM());
      il.append(new ILOAD(_currentIndex));
      il.append(new INVOKEINTERFACE(getNS, 2));
      il.append(new SWITCH(types, targets, defaultTarget));
      return (il);
    } else {
      return (null);
    }
  }

  /**
   * Compiles the applyTemplates() method and adds it to the translet.
   * This is the main dispatch method.
   */
  public void compileApplyTemplates(ClassGenerator classGen) {
    final XSLTC xsltc = classGen.getParser().getXSLTC();
    final ConstantPoolGen cpg = classGen.getConstantPool();
    final Vector names = xsltc.getNamesIndex();

    // Create the applyTemplates() method
    final com.sun.org.apache.bcel.internal.generic.Type[] argTypes =
        new com.sun.org.apache.bcel.internal.generic.Type[3];
    argTypes[0] = Util.getJCRefType(DOM_INTF_SIG);
    argTypes[1] = Util.getJCRefType(NODE_ITERATOR_SIG);
    argTypes[2] = Util.getJCRefType(TRANSLET_OUTPUT_SIG);

    final String[] argNames = new String[3];
    argNames[0] = DOCUMENT_PNAME;
    argNames[1] = ITERATOR_PNAME;
    argNames[2] = TRANSLET_OUTPUT_PNAME;

    final InstructionList mainIL = new InstructionList();
    final MethodGenerator methodGen =
        new MethodGenerator(ACC_PUBLIC | ACC_FINAL,
            com.sun.org.apache.bcel.internal.generic.Type.VOID,
            argTypes, argNames, functionName(),
            getClassName(), mainIL,
            classGen.getConstantPool());
    methodGen.addException("com.sun.org.apache.xalan.internal.xsltc.TransletException");
    // Insert an extra NOP just to keep "current" from appearing as if it
    // has a value before the start of the loop.
    mainIL.append(NOP);

    // Create a local variable to hold the current node
    final LocalVariableGen current;
    current = methodGen.addLocalVariable2("current",
        com.sun.org.apache.bcel.internal.generic.Type.INT,
        null);
    _currentIndex = current.getIndex();

    // Create the "body" instruction list that will eventually hold the
    // code for the entire method (other ILs will be appended).
    final InstructionList body = new InstructionList();
    body.append(NOP);

    // Create an instruction list that contains the default next-node
    // iteration
    final InstructionList ilLoop = new InstructionList();
    ilLoop.append(methodGen.loadIterator());
    ilLoop.append(methodGen.nextNode());
    ilLoop.append(DUP);
    ilLoop.append(new ISTORE(_currentIndex));

    // The body of this code can get very large - large than can be handled
    // by a single IFNE(body.getStart()) instruction - need workaround:
    final BranchHandle ifeq = ilLoop.append(new IFLT(null));
    final BranchHandle loop = ilLoop.append(new GOTO_W(null));
    ifeq.setTarget(ilLoop.append(RETURN));  // applyTemplates() ends here!
    final InstructionHandle ihLoop = ilLoop.getStart();

    current.setStart(mainIL.append(new GOTO_W(ihLoop)));

    // Live range of "current" ends at end of loop
    current.setEnd(loop);

    // Compile default handling of elements (traverse children)
    InstructionList ilRecurse =
        compileDefaultRecursion(classGen, methodGen, ihLoop);
    InstructionHandle ihRecurse = ilRecurse.getStart();

    // Compile default handling of text/attribute nodes (output text)
    InstructionList ilText =
        compileDefaultText(classGen, methodGen, ihLoop);
    InstructionHandle ihText = ilText.getStart();

    // Distinguish attribute/element/namespace tests for further processing
    final int[] types = new int[DTM.NTYPES + names.size()];
    for (int i = 0; i < types.length; i++) {
      types[i] = i;
    }

    // Initialize isAttribute[] and isNamespace[] arrays
    final boolean[] isAttribute = new boolean[types.length];
    final boolean[] isNamespace = new boolean[types.length];
    for (int i = 0; i < names.size(); i++) {
      final String name = (String) names.elementAt(i);
      isAttribute[i + DTM.NTYPES] = isAttributeName(name);
      isNamespace[i + DTM.NTYPES] = isNamespaceName(name);
    }

    // Compile all templates - regardless of pattern type
    compileTemplates(classGen, methodGen, ihLoop);

    // Handle template with explicit "*" pattern
    final TestSeq elemTest = _testSeq[DTM.ELEMENT_NODE];
    InstructionHandle ihElem = ihRecurse;
    if (elemTest != null) {
      ihElem = elemTest.compile(classGen, methodGen, ihRecurse);
    }

    // Handle template with explicit "@*" pattern
    final TestSeq attrTest = _testSeq[DTM.ATTRIBUTE_NODE];
    InstructionHandle ihAttr = ihText;
    if (attrTest != null) {
      ihAttr = attrTest.compile(classGen, methodGen, ihAttr);
    }

    // Do tests for id() and key() patterns first
    InstructionList ilKey = null;
    if (_idxTestSeq != null) {
      loop.setTarget(_idxTestSeq.compile(classGen, methodGen, body.getStart()));
      ilKey = _idxTestSeq.getInstructionList();
    } else {
      loop.setTarget(body.getStart());
    }

    // If there is a match on node() we need to replace ihElem
    // and ihText if the priority of node() is higher
    if (_childNodeTestSeq != null) {
      // Compare priorities of node() and "*"
      double nodePrio = _childNodeTestSeq.getPriority();
      int nodePos = _childNodeTestSeq.getPosition();
      double elemPrio = (0 - Double.MAX_VALUE);
      int elemPos = Integer.MIN_VALUE;

      if (elemTest != null) {
        elemPrio = elemTest.getPriority();
        elemPos = elemTest.getPosition();
      }
      if (elemPrio == Double.NaN || elemPrio < nodePrio ||
          (elemPrio == nodePrio && elemPos < nodePos)) {
        ihElem = _childNodeTestSeq.compile(classGen, methodGen, ihLoop);
      }

      // Compare priorities of node() and text()
      final TestSeq textTest = _testSeq[DTM.TEXT_NODE];
      double textPrio = (0 - Double.MAX_VALUE);
      int textPos = Integer.MIN_VALUE;

      if (textTest != null) {
        textPrio = textTest.getPriority();
        textPos = textTest.getPosition();
      }
      if (textPrio == Double.NaN || textPrio < nodePrio ||
          (textPrio == nodePrio && textPos < nodePos)) {
        ihText = _childNodeTestSeq.compile(classGen, methodGen, ihLoop);
        _testSeq[DTM.TEXT_NODE] = _childNodeTestSeq;
      }
    }

    // Handle templates with "ns:*" pattern
    InstructionHandle elemNamespaceHandle = ihElem;
    InstructionList nsElem = compileNamespaces(classGen, methodGen,
        isNamespace, isAttribute,
        false, ihElem);
    if (nsElem != null) {
      elemNamespaceHandle = nsElem.getStart();
    }

    // Handle templates with "ns:@*" pattern
    InstructionHandle attrNamespaceHandle = ihAttr;
    InstructionList nsAttr = compileNamespaces(classGen, methodGen,
        isNamespace, isAttribute,
        true, ihAttr);
    if (nsAttr != null) {
      attrNamespaceHandle = nsAttr.getStart();
    }

    // Handle templates with "ns:elem" or "ns:@attr" pattern
    final InstructionHandle[] targets = new InstructionHandle[types.length];
    for (int i = DTM.NTYPES; i < targets.length; i++) {
      final TestSeq testSeq = _testSeq[i];
      // Jump straight to namespace tests ?
      if (isNamespace[i]) {
        if (isAttribute[i]) {
          targets[i] = attrNamespaceHandle;
        } else {
          targets[i] = elemNamespaceHandle;
        }
      }
      // Test first, then jump to namespace tests
      else if (testSeq != null) {
        if (isAttribute[i]) {
          targets[i] = testSeq.compile(classGen, methodGen,
              attrNamespaceHandle);
        } else {
          targets[i] = testSeq.compile(classGen, methodGen,
              elemNamespaceHandle);
        }
      } else {
        targets[i] = ihLoop;
      }
    }

    // Handle pattern with match on root node - default: traverse children
    targets[DTM.ROOT_NODE] = _rootPattern != null
        ? getTemplateInstructionHandle(_rootPattern.getTemplate())
        : ihRecurse;

    // Handle pattern with match on root node - default: traverse children
    targets[DTM.DOCUMENT_NODE] = _rootPattern != null
        ? getTemplateInstructionHandle(_rootPattern.getTemplate())
        : ihRecurse;

    // Handle any pattern with match on text nodes - default: output text
    targets[DTM.TEXT_NODE] = _testSeq[DTM.TEXT_NODE] != null
        ? _testSeq[DTM.TEXT_NODE].compile(classGen, methodGen, ihText)
        : ihText;

    // This DOM-type is not in use - default: process next node
    targets[DTM.NAMESPACE_NODE] = ihLoop;

    // Match unknown element in DOM - default: check for namespace match
    targets[DTM.ELEMENT_NODE] = elemNamespaceHandle;

    // Match unknown attribute in DOM - default: check for namespace match
    targets[DTM.ATTRIBUTE_NODE] = attrNamespaceHandle;

    // Match on processing instruction - default: process next node
    InstructionHandle ihPI = ihLoop;
    if (_childNodeTestSeq != null) {
      ihPI = ihElem;
    }
    if (_testSeq[DTM.PROCESSING_INSTRUCTION_NODE] != null) {
      targets[DTM.PROCESSING_INSTRUCTION_NODE] =
          _testSeq[DTM.PROCESSING_INSTRUCTION_NODE].
              compile(classGen, methodGen, ihPI);
    } else {
      targets[DTM.PROCESSING_INSTRUCTION_NODE] = ihPI;
    }

    // Match on comments - default: process next node
    InstructionHandle ihComment = ihLoop;
    if (_childNodeTestSeq != null) {
      ihComment = ihElem;
    }
    targets[DTM.COMMENT_NODE] = _testSeq[DTM.COMMENT_NODE] != null
        ? _testSeq[DTM.COMMENT_NODE].compile(classGen, methodGen, ihComment)
        : ihComment;

    // This DOM-type is not in use - default: process next node
    targets[DTM.CDATA_SECTION_NODE] = ihLoop;

    // This DOM-type is not in use - default: process next node
    targets[DTM.DOCUMENT_FRAGMENT_NODE] = ihLoop;

    // This DOM-type is not in use - default: process next node
    targets[DTM.DOCUMENT_TYPE_NODE] = ihLoop;

    // This DOM-type is not in use - default: process next node
    targets[DTM.ENTITY_NODE] = ihLoop;

    // This DOM-type is not in use - default: process next node
    targets[DTM.ENTITY_REFERENCE_NODE] = ihLoop;

    // This DOM-type is not in use - default: process next node
    targets[DTM.NOTATION_NODE] = ihLoop;

    // Now compile test sequences for various match patterns:
    for (int i = DTM.NTYPES; i < targets.length; i++) {
      final TestSeq testSeq = _testSeq[i];
      // Jump straight to namespace tests ?
      if ((testSeq == null) || (isNamespace[i])) {
        if (isAttribute[i]) {
          targets[i] = attrNamespaceHandle;
        } else {
          targets[i] = elemNamespaceHandle;
        }
      }
      // Match on node type
      else {
        if (isAttribute[i]) {
          targets[i] = testSeq.compile(classGen, methodGen,
              attrNamespaceHandle);
        } else {
          targets[i] = testSeq.compile(classGen, methodGen,
              elemNamespaceHandle);
        }
      }
    }

    if (ilKey != null) {
      body.insert(ilKey);
    }

    // Append first code in applyTemplates() - get type of current node
    final int getType = cpg.addInterfaceMethodref(DOM_INTF,
        "getExpandedTypeID",
        "(I)I");
    body.append(methodGen.loadDOM());
    body.append(new ILOAD(_currentIndex));
    body.append(new INVOKEINTERFACE(getType, 2));

    // Append switch() statement - main dispatch loop in applyTemplates()
    InstructionHandle disp = body.append(new SWITCH(types, targets, ihLoop));

    // Append all the "case:" statements
    appendTestSequences(body);
    // Append the actual template code
    appendTemplateCode(body);

    // Append NS:* node tests (if any)
    if (nsElem != null) {
      body.append(nsElem);
    }
    // Append NS:@* node tests (if any)
    if (nsAttr != null) {
      body.append(nsAttr);
    }

    // Append default action for element and root nodes
    body.append(ilRecurse);
    // Append default action for text and attribute nodes
    body.append(ilText);

    // putting together constituent instruction lists
    mainIL.append(body);
    // fall through to ilLoop
    mainIL.append(ilLoop);

    peepHoleOptimization(methodGen);
    classGen.addMethod(methodGen);

    // Compile method(s) for <xsl:apply-imports/> for this mode
    if (_importLevels != null) {
      Enumeration levels = _importLevels.keys();
      while (levels.hasMoreElements()) {
        Integer max = (Integer) levels.nextElement();
        Integer min = (Integer) _importLevels.get(max);
        compileApplyImports(classGen, min.intValue(), max.intValue());
      }
    }
  }

  private void compileTemplateCalls(ClassGenerator classGen,
      MethodGenerator methodGen,
      InstructionHandle next, int min, int max) {
    Enumeration templates = _neededTemplates.keys();
    while (templates.hasMoreElements()) {
      final Template template = (Template) templates.nextElement();
      final int prec = template.getImportPrecedence();
      if ((prec >= min) && (prec < max)) {
        if (template.hasContents()) {
          InstructionList til = template.compile(classGen, methodGen);
          til.append(new GOTO_W(next));
          _templateILs.put(template, til);
          _templateIHs.put(template, til.getStart());
        } else {
          // empty template
          _templateIHs.put(template, next);
        }
      }
    }
  }


  public void compileApplyImports(ClassGenerator classGen, int min, int max) {
    final XSLTC xsltc = classGen.getParser().getXSLTC();
    final ConstantPoolGen cpg = classGen.getConstantPool();
    final Vector names = xsltc.getNamesIndex();

    // Clear some datastructures
    _namedTemplates = new Hashtable();
    _neededTemplates = new Hashtable();
    _templateIHs = new Hashtable();
    _templateILs = new Hashtable();
    _patternGroups = new Vector[32];
    _rootPattern = null;

    // IMPORTANT: Save orignal & complete set of templates!!!!
    Vector oldTemplates = _templates;

    // Gather templates that are within the scope of this import
    _templates = new Vector();
    final Enumeration templates = oldTemplates.elements();
    while (templates.hasMoreElements()) {
      final Template template = (Template) templates.nextElement();
      final int prec = template.getImportPrecedence();
      if ((prec >= min) && (prec < max)) {
        addTemplate(template);
      }
    }

    // Process all patterns from those templates
    processPatterns(_keys);

    // Create the applyTemplates() method
    final com.sun.org.apache.bcel.internal.generic.Type[] argTypes =
        new com.sun.org.apache.bcel.internal.generic.Type[4];
    argTypes[0] = Util.getJCRefType(DOM_INTF_SIG);
    argTypes[1] = Util.getJCRefType(NODE_ITERATOR_SIG);
    argTypes[2] = Util.getJCRefType(TRANSLET_OUTPUT_SIG);
    argTypes[3] = com.sun.org.apache.bcel.internal.generic.Type.INT;

    final String[] argNames = new String[4];
    argNames[0] = DOCUMENT_PNAME;
    argNames[1] = ITERATOR_PNAME;
    argNames[2] = TRANSLET_OUTPUT_PNAME;
    argNames[3] = NODE_PNAME;

    final InstructionList mainIL = new InstructionList();
    final MethodGenerator methodGen =
        new MethodGenerator(ACC_PUBLIC | ACC_FINAL,
            com.sun.org.apache.bcel.internal.generic.Type.VOID,
            argTypes, argNames, functionName() + '_' + max,
            getClassName(), mainIL,
            classGen.getConstantPool());
    methodGen.addException("com.sun.org.apache.xalan.internal.xsltc.TransletException");

    // Create the local variable to hold the current node
    final LocalVariableGen current;
    current = methodGen.addLocalVariable2("current",
        com.sun.org.apache.bcel.internal.generic.Type.INT,
        null);
    _currentIndex = current.getIndex();

    mainIL.append(new ILOAD(methodGen.getLocalIndex(NODE_PNAME)));
    current.setStart(mainIL.append(new ISTORE(_currentIndex)));

    // Create the "body" instruction list that will eventually hold the
    // code for the entire method (other ILs will be appended).
    final InstructionList body = new InstructionList();
    body.append(NOP);

    // Create an instruction list that contains the default next-node
    // iteration
    final InstructionList ilLoop = new InstructionList();
    ilLoop.append(RETURN);
    final InstructionHandle ihLoop = ilLoop.getStart();

    // Compile default handling of elements (traverse children)
    InstructionList ilRecurse =
        compileDefaultRecursion(classGen, methodGen, ihLoop);
    InstructionHandle ihRecurse = ilRecurse.getStart();

    // Compile default handling of text/attribute nodes (output text)
    InstructionList ilText =
        compileDefaultText(classGen, methodGen, ihLoop);
    InstructionHandle ihText = ilText.getStart();

    // Distinguish attribute/element/namespace tests for further processing
    final int[] types = new int[DTM.NTYPES + names.size()];
    for (int i = 0; i < types.length; i++) {
      types[i] = i;
    }

    final boolean[] isAttribute = new boolean[types.length];
    final boolean[] isNamespace = new boolean[types.length];
    for (int i = 0; i < names.size(); i++) {
      final String name = (String) names.elementAt(i);
      isAttribute[i + DTM.NTYPES] = isAttributeName(name);
      isNamespace[i + DTM.NTYPES] = isNamespaceName(name);
    }

    // Compile all templates - regardless of pattern type
    compileTemplateCalls(classGen, methodGen, ihLoop, min, max);

    // Handle template with explicit "*" pattern
    final TestSeq elemTest = _testSeq[DTM.ELEMENT_NODE];
    InstructionHandle ihElem = ihRecurse;
    if (elemTest != null) {
      ihElem = elemTest.compile(classGen, methodGen, ihLoop);
    }

    // Handle template with explicit "@*" pattern
    final TestSeq attrTest = _testSeq[DTM.ATTRIBUTE_NODE];
    InstructionHandle ihAttr = ihLoop;
    if (attrTest != null) {
      ihAttr = attrTest.compile(classGen, methodGen, ihAttr);
    }

    // Do tests for id() and key() patterns first
    InstructionList ilKey = null;
    if (_idxTestSeq != null) {
      ilKey = _idxTestSeq.getInstructionList();
    }

    // If there is a match on node() we need to replace ihElem
    // and ihText if the priority of node() is higher
    if (_childNodeTestSeq != null) {
      // Compare priorities of node() and "*"
      double nodePrio = _childNodeTestSeq.getPriority();
      int nodePos = _childNodeTestSeq.getPosition();
      double elemPrio = (0 - Double.MAX_VALUE);
      int elemPos = Integer.MIN_VALUE;

      if (elemTest != null) {
        elemPrio = elemTest.getPriority();
        elemPos = elemTest.getPosition();
      }

      if (elemPrio == Double.NaN || elemPrio < nodePrio ||
          (elemPrio == nodePrio && elemPos < nodePos)) {
        ihElem = _childNodeTestSeq.compile(classGen, methodGen, ihLoop);
      }

      // Compare priorities of node() and text()
      final TestSeq textTest = _testSeq[DTM.TEXT_NODE];
      double textPrio = (0 - Double.MAX_VALUE);
      int textPos = Integer.MIN_VALUE;

      if (textTest != null) {
        textPrio = textTest.getPriority();
        textPos = textTest.getPosition();
      }

      if (textPrio == Double.NaN || textPrio < nodePrio ||
          (textPrio == nodePrio && textPos < nodePos)) {
        ihText = _childNodeTestSeq.compile(classGen, methodGen, ihLoop);
        _testSeq[DTM.TEXT_NODE] = _childNodeTestSeq;
      }
    }

    // Handle templates with "ns:*" pattern
    InstructionHandle elemNamespaceHandle = ihElem;
    InstructionList nsElem = compileNamespaces(classGen, methodGen,
        isNamespace, isAttribute,
        false, ihElem);
    if (nsElem != null) {
      elemNamespaceHandle = nsElem.getStart();
    }

    // Handle templates with "ns:@*" pattern
    InstructionList nsAttr = compileNamespaces(classGen, methodGen,
        isNamespace, isAttribute,
        true, ihAttr);
    InstructionHandle attrNamespaceHandle = ihAttr;
    if (nsAttr != null) {
      attrNamespaceHandle = nsAttr.getStart();
    }

    // Handle templates with "ns:elem" or "ns:@attr" pattern
    final InstructionHandle[] targets = new InstructionHandle[types.length];
    for (int i = DTM.NTYPES; i < targets.length; i++) {
      final TestSeq testSeq = _testSeq[i];
      // Jump straight to namespace tests ?
      if (isNamespace[i]) {
        if (isAttribute[i]) {
          targets[i] = attrNamespaceHandle;
        } else {
          targets[i] = elemNamespaceHandle;
        }
      }
      // Test first, then jump to namespace tests
      else if (testSeq != null) {
        if (isAttribute[i]) {
          targets[i] = testSeq.compile(classGen, methodGen,
              attrNamespaceHandle);
        } else {
          targets[i] = testSeq.compile(classGen, methodGen,
              elemNamespaceHandle);
        }
      } else {
        targets[i] = ihLoop;
      }
    }

    // Handle pattern with match on root node - default: traverse children
    targets[DTM.ROOT_NODE] = _rootPattern != null
        ? getTemplateInstructionHandle(_rootPattern.getTemplate())
        : ihRecurse;
    // Handle pattern with match on root node - default: traverse children
    targets[DTM.DOCUMENT_NODE] = _rootPattern != null
        ? getTemplateInstructionHandle(_rootPattern.getTemplate())
        : ihRecurse;    // %HZ%:  Was ihLoop in XSLTC_DTM branch

    // Handle any pattern with match on text nodes - default: loop
    targets[DTM.TEXT_NODE] = _testSeq[DTM.TEXT_NODE] != null
        ? _testSeq[DTM.TEXT_NODE].compile(classGen, methodGen, ihText)
        : ihText;

    // This DOM-type is not in use - default: process next node
    targets[DTM.NAMESPACE_NODE] = ihLoop;

    // Match unknown element in DOM - default: check for namespace match
    targets[DTM.ELEMENT_NODE] = elemNamespaceHandle;

    // Match unknown attribute in DOM - default: check for namespace match
    targets[DTM.ATTRIBUTE_NODE] = attrNamespaceHandle;

    // Match on processing instruction - default: loop
    InstructionHandle ihPI = ihLoop;
    if (_childNodeTestSeq != null) {
      ihPI = ihElem;
    }
    if (_testSeq[DTM.PROCESSING_INSTRUCTION_NODE] != null) {
      targets[DTM.PROCESSING_INSTRUCTION_NODE] =
          _testSeq[DTM.PROCESSING_INSTRUCTION_NODE].
              compile(classGen, methodGen, ihPI);
    } else {
      targets[DTM.PROCESSING_INSTRUCTION_NODE] = ihPI;
    }

    // Match on comments - default: process next node
    InstructionHandle ihComment = ihLoop;
    if (_childNodeTestSeq != null) {
      ihComment = ihElem;
    }
    targets[DTM.COMMENT_NODE] = _testSeq[DTM.COMMENT_NODE] != null
        ? _testSeq[DTM.COMMENT_NODE].compile(classGen, methodGen, ihComment)
        : ihComment;

    // This DOM-type is not in use - default: process next node
    targets[DTM.CDATA_SECTION_NODE] = ihLoop;

    // This DOM-type is not in use - default: process next node
    targets[DTM.DOCUMENT_FRAGMENT_NODE] = ihLoop;

    // This DOM-type is not in use - default: process next node
    targets[DTM.DOCUMENT_TYPE_NODE] = ihLoop;

    // This DOM-type is not in use - default: process next node
    targets[DTM.ENTITY_NODE] = ihLoop;

    // This DOM-type is not in use - default: process next node
    targets[DTM.ENTITY_REFERENCE_NODE] = ihLoop;

    // This DOM-type is not in use - default: process next node
    targets[DTM.NOTATION_NODE] = ihLoop;

    // Now compile test sequences for various match patterns:
    for (int i = DTM.NTYPES; i < targets.length; i++) {
      final TestSeq testSeq = _testSeq[i];
      // Jump straight to namespace tests ?
      if ((testSeq == null) || (isNamespace[i])) {
        if (isAttribute[i]) {
          targets[i] = attrNamespaceHandle;
        } else {
          targets[i] = elemNamespaceHandle;
        }
      }
      // Match on node type
      else {
        if (isAttribute[i]) {
          targets[i] = testSeq.compile(classGen, methodGen,
              attrNamespaceHandle);
        } else {
          targets[i] = testSeq.compile(classGen, methodGen,
              elemNamespaceHandle);
        }
      }
    }

    if (ilKey != null) {
      body.insert(ilKey);
    }

    // Append first code in applyTemplates() - get type of current node
    final int getType = cpg.addInterfaceMethodref(DOM_INTF,
        "getExpandedTypeID",
        "(I)I");
    body.append(methodGen.loadDOM());
    body.append(new ILOAD(_currentIndex));
    body.append(new INVOKEINTERFACE(getType, 2));

    // Append switch() statement - main dispatch loop in applyTemplates()
    InstructionHandle disp = body.append(new SWITCH(types, targets, ihLoop));

    // Append all the "case:" statements
    appendTestSequences(body);
    // Append the actual template code
    appendTemplateCode(body);

    // Append NS:* node tests (if any)
    if (nsElem != null) {
      body.append(nsElem);
    }
    // Append NS:@* node tests (if any)
    if (nsAttr != null) {
      body.append(nsAttr);
    }

    // Append default action for element and root nodes
    body.append(ilRecurse);
    // Append default action for text and attribute nodes
    body.append(ilText);

    // putting together constituent instruction lists
    mainIL.append(body);

    // Mark the end of the live range for the "current" variable
    current.setEnd(body.getEnd());

    // fall through to ilLoop
    mainIL.append(ilLoop);

    peepHoleOptimization(methodGen);
    classGen.addMethod(methodGen);

    // Restore original (complete) set of templates for this transformation
    _templates = oldTemplates;
  }

  /**
   * Peephole optimization.
   */
  private void peepHoleOptimization(MethodGenerator methodGen) {
    InstructionList il = methodGen.getInstructionList();
    InstructionFinder find = new InstructionFinder(il);
    InstructionHandle ih;
    String pattern;

    // LoadInstruction, POP => (removed)
    // pattern = "LoadInstruction POP";
    // changed to lower case - changing to all lower case although only the instruction with capital I
    // is creating a problem in the Turkish locale
    pattern = "loadinstruction pop";

    for (Iterator iter = find.search(pattern); iter.hasNext(); ) {
      InstructionHandle[] match = (InstructionHandle[]) iter.next();
      try {
        if (!match[0].hasTargeters() && !match[1].hasTargeters()) {
          il.delete(match[0], match[1]);
        }
      } catch (TargetLostException e) {
        // TODO: move target down into the list
      }
    }

    // ILOAD_N, ILOAD_N, SWAP, ISTORE_N => ILOAD_N
    // pattern = "ILOAD ILOAD SWAP ISTORE";
    // changed to lower case - changing to all lower case although only the instruction with capital I
    // is creating a problem in the Turkish locale
    pattern = "iload iload swap istore";
    for (Iterator iter = find.search(pattern); iter.hasNext(); ) {
      InstructionHandle[] match = (InstructionHandle[]) iter.next();
      try {
        com.sun.org.apache.bcel.internal.generic.ILOAD iload1 =
            (com.sun.org.apache.bcel.internal.generic.ILOAD) match[0].getInstruction();
        com.sun.org.apache.bcel.internal.generic.ILOAD iload2 =
            (com.sun.org.apache.bcel.internal.generic.ILOAD) match[1].getInstruction();
        com.sun.org.apache.bcel.internal.generic.ISTORE istore =
            (com.sun.org.apache.bcel.internal.generic.ISTORE) match[3].getInstruction();

        if (!match[1].hasTargeters() &&
            !match[2].hasTargeters() &&
            !match[3].hasTargeters() &&
            iload1.getIndex() == iload2.getIndex() &&
            iload2.getIndex() == istore.getIndex()) {
          il.delete(match[1], match[3]);
        }
      } catch (TargetLostException e) {
        // TODO: move target down into the list
      }
    }

    // LoadInstruction_N, LoadInstruction_M, SWAP => LoadInstruction_M, LoadInstruction_N
    // pattern = "LoadInstruction LoadInstruction SWAP";
    // changed to lower case - changing to all lower case although only the instruction with capital I
    // is creating a problem in the Turkish locale
    pattern = "loadinstruction loadinstruction swap";
    for (Iterator iter = find.search(pattern); iter.hasNext(); ) {
      InstructionHandle[] match = (InstructionHandle[]) iter.next();
      try {
        if (!match[0].hasTargeters() &&
            !match[1].hasTargeters() &&
            !match[2].hasTargeters()) {
          Instruction load_m = match[1].getInstruction();
          il.insert(match[0], load_m);
          il.delete(match[1], match[2]);
        }
      } catch (TargetLostException e) {
        // TODO: move target down into the list
      }
    }

    // ALOAD_N ALOAD_N => ALOAD_N DUP
    // pattern = "ALOAD ALOAD";
    // changed to lower case - changing to all lower case although only the instruction with capital I
    // is creating a problem in the Turkish locale
    pattern = "aload aload";
    for (Iterator iter = find.search(pattern); iter.hasNext(); ) {
      InstructionHandle[] match = (InstructionHandle[]) iter.next();
      try {
        if (!match[1].hasTargeters()) {
          com.sun.org.apache.bcel.internal.generic.ALOAD aload1 =
              (com.sun.org.apache.bcel.internal.generic.ALOAD) match[0].getInstruction();
          com.sun.org.apache.bcel.internal.generic.ALOAD aload2 =
              (com.sun.org.apache.bcel.internal.generic.ALOAD) match[1].getInstruction();

          if (aload1.getIndex() == aload2.getIndex()) {
            il.insert(match[1], new DUP());
            il.delete(match[1]);
          }
        }
      } catch (TargetLostException e) {
        // TODO: move target down into the list
      }
    }
  }

  public InstructionHandle getTemplateInstructionHandle(Template template) {
    return (InstructionHandle) _templateIHs.get(template);
  }

  /**
   * Auxiliary method to determine if a qname is an attribute.
   */
  private static boolean isAttributeName(String qname) {
    final int col = qname.lastIndexOf(':') + 1;
    return (qname.charAt(col) == '@');
  }

  /**
   * Auxiliary method to determine if a qname is a namespace
   * qualified "*".
   */
  private static boolean isNamespaceName(String qname) {
    final int col = qname.lastIndexOf(':');
    return (col > -1 && qname.charAt(qname.length() - 1) == '*');
  }
}
